/**
 * 普通工具函数
 */
import { isObject, isEmpty, isString, isFunction } from 'lodash-es'

/**
 * @function  取URL参数
 * @param     { String | undefined } paramName 传入需要获取参数
 * @returns   { String | Object }      传入了参数名称返回对应的value 返回object
 * @example   getUrlParams()
 */
export function getUrlParams(paramName?: string): string | { [key: string]: string } {
  if (paramName === null || (!isString(paramName) && paramName)) {
    return ''
  }
  const href = decodeURI(window.location.href)
  let params = ''
  let arr = []
  let result: any = {}
  const setObjValue = (arr: any[], obj: any) => {
    for (let i = 0; i < arr.length; i++) {
      const o = arr[i]
      const list = o.split('=')
      if (list.length === 2) {
        obj[list[0]] = list[1]
      } else {
        setObjValue(o.split('?'), obj)
      }
    }
    return obj
  }
  if (href.indexOf('?') < 0) {
    return paramName ? '' : {}
  }

  params = href.substring(href.indexOf('?') + 1)
  arr = params.split('&')
  if (!params.includes('=') || !Array.isArray(arr) || !arr.length) {
    return paramName ? '' : {}
  }
  result = setObjValue(arr, {})
  if (paramName) {
    return result[paramName] ? result[paramName] : ''
  }
  return result
}

/**
 * @function   对象转URL参数格式
 * @param      { Object }   data 需要转换的object 对象的值不能是引用类型
 * @param      { Boolean }  symbol 是否需要在转换结果的前面加 '?'
 * @returns    { String }   转换好的url参数字符串
 * @example   objectToUrlParams({}, true)
 */
export function objectToUrlParams(data: { [key: string]: string | number }, symbol = false): string {
  if (!isObject(data) || isEmpty(data)) {
    return ''
  }
  let params: any = ''
  for (const key in data) {
    params += `${key}=${data[key]}&`
  }
  params = params.substring(0, params.lastIndexOf('&'))
  return symbol ? `?${params}` : params
}

/**
 * @function 数字保留小数位数
 * @param    { String | Number }  value 需要处理的数字
 * @param    { Number }           decimalPlaces 保留几位小数
 * @param    { Boolean }          halfAdjust 是否四舍五入
 * @param    { Boolean }          integerToDecimal 整数是否也添加小数位
 * @returns  { String }           处理好的小数位数数据
 * @example   preserveDecimalLaces(1.11, 2, false, false)
 */
export function preserveDecimalLaces(
  value: string | number = '0',
  decimalPlaces: number = 2,
  halfAdjust = true,
  integerToDecimal = false,
): string {
  // 类型和有效性检查
  if ((typeof value !== 'string' && typeof value !== 'number') || 
      typeof decimalPlaces !== 'number' || 
      decimalPlaces < 0 ||
      decimalPlaces > 20 || // 限制最大小数位，避免精度问题
      value === '' || 
      isNaN(Number(value))) {
    return '';
  }

  const num = Number(value);
  let processedNum: number;

  // 处理四舍五入或截断 - 使用字符串处理避免浮点数精度问题
  if (halfAdjust) {
    // 四舍五入处理
    processedNum = Number(num.toFixed(decimalPlaces));
  } else {
    // 截断处理 - 使用字符串操作确保精度
    const numStr = num.toString();
    const decimalIndex = numStr.indexOf('.');
    
    if (decimalIndex === -1) {
      // 没有小数部分
      processedNum = num;
    } else {
      // 有小数部分，截断到指定位数
      const integerPart = numStr.substring(0, decimalIndex);
      const decimalPart = numStr.substring(decimalIndex + 1, decimalIndex + 1 + decimalPlaces);
      const truncatedStr = decimalPart ? `${integerPart}.${decimalPart}` : integerPart;
      processedNum = Number(truncatedStr);
    }
  }

  // 转换为字符串并处理小数位
  let numStr = processedNum.toString();
  const hasDecimal = numStr.includes('.');

  // 如果需要强制显示小数位，或者本身有小数位
  if (integerToDecimal || hasDecimal) {
    const parts = numStr.split('.');
    const integerPart = parts[0];
    let decimalPart = parts[1] || '';

    // 补足小数位
    if (decimalPart.length < decimalPlaces) {
      decimalPart = decimalPart.padEnd(decimalPlaces, '0');
    }

    numStr = `${integerPart}.${decimalPart}`;
  }

  return numStr;
}

/**
 * @function 复制文本到剪切板
 * @param    { String }   text - 需要复制的文本
 * @returns  { Promise }  Promise<string>
 * @example  copyText('你好！')
 */
export function copyText(text: string): Promise<string> {
  return new Promise((resolve, reject) => {
    if (!isString(text) || !text) {
      reject(!isString(text) ? '只能复制文本' : '文本不能为空')
    }
    if (navigator.clipboard && isFunction(navigator.clipboard.writeText)) {
      navigator.clipboard
        .writeText(text)
        .then(() => {
          resolve('复制成功')
        })
        .catch(() => {
          reject('复制失败')
        })
    } else if (isFunction(document.execCommand)) {
      const textarea = document.createElement('textarea')
      textarea.style.position = 'fixed'
      textarea.style.left = '0'
      textarea.style.top = '0'
      textarea.style.opacity = '0'
      textarea.style.zIndex = '-999999'
      textarea.style.height = '0px'
      textarea.value = text
      document.body.appendChild(textarea)
      textarea.select()
      if (document.execCommand('Copy')) {
        resolve('复制成功')
        return
      }
      reject('复制失败')
    } else {
      reject('复制失败')
    }
  })
}

/**
 * @function file对象转为base64字符串
 * @param    { File }     file 需要转换的File对象
 * @returns  { Promise }  Promise<{ baseUrl: string; atobUrl: string }>
 * @example  fileToBase64(File)
 */
export function fileToBase64(file: File): Promise<{ baseUrl: string; atobUrl: string }> {
  return new Promise((resolve, reject) => {
    if (!(file instanceof File)) {
      reject('请传入File')
    }
    const reader: any = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = () => {
      resolve({
        baseUrl: reader.result as string,
        atobUrl: reader.result.split(',')[1],
      })
    }
    reader.onerror = (err: any) => {
      reject(err)
    }
  })
}

/**
 *
 * @function  file对象转为Blob对象
 * @param     { File }      需要转换的File对象
 * @returns   { Promise }   Promise<{blob: Blob, url:string}>
 * @example   fileToBlob(File)
 */
export function fileToBlob(file: File): Promise<{ blob: Blob; url: string }> {
  return new Promise((resolve, reject) => {
    if (!(file instanceof File)) {
      reject('请传入File')
    }
    const reader = new FileReader()
    reader.readAsArrayBuffer(file)
    reader.onload = (e: any) => {
      const blob: any = new Blob([e.target.result], { type: file.type })
      resolve({
        blob,
        url: window.URL.createObjectURL(blob),
      })
    }
    reader.onerror = (err) => {
      reject(err)
    }
  })
}

/**
 *
 * @function  file对象转为ArrayBuffer对象
 * @param     { File }      需要转换的File对象
 * @returns   { Promise }   Promise<ArrayBuffer>
 * @example  fileToArrayBuffer(File)
 */
export function fileToArrayBuffer(file: File): Promise<ArrayBuffer> {
  return new Promise((resolve, reject) => {
    if (!(file instanceof File)) {
      reject('请传入File')
    }
    const reader = new FileReader()
    reader.readAsArrayBuffer(file)
    reader.onload = (e: any) => {
      resolve(e.target.result)
    }
    reader.onerror = (err) => {
      reject(err)
    }
  })
}

/**
 * @function  base64转file
 * @param     { String }    需要转换的base64
 * @param     { String }    文件名
 * @returns   { File }      文件对象
 * @example   base64ToFile(String)
 */
export function base64ToFile(base64Url: string, name: string = 'file'): File | null {
  if (!base64Url || !isString(base64Url)) {
    return null
  }
  const arr: any[] = base64Url.split(',')
  const mime = arr[0].match(/:(.*?);/)[1]
  name += '.' + mime.split('/')[1]
  const bstr = window.atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new File([u8arr], name, {
    type: mime,
  })
}

/**
 * @function    base64转Blob
 * @param       { String }    需要转换的base64
 * @returns     { Blob  }     Blob对象
 * @example     base64ToBlob(String)
 */
export function base64ToBlob(base64Url: string): Blob | null {
  if (!base64Url || !isString(base64Url)) {
    return null
  }
  const byteString = window.atob(base64Url.split(',')[1])
  const mimeString = base64Url.split(',')[0].split(':')[1].split(';')[0]
  const ab = new ArrayBuffer(byteString.length)
  const ia = new Uint8Array(ab)
  for (let i = 0; i < byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i)
  }
  return new Blob([ab], { type: mimeString })
}

/**
 * @function    base64转ArrayBuffer
 * @param       { String }            需要转换的base64
 * @returns     { ArrayBuffer  }      ArrayBuffer对象
 * @example     base64ToArrayBuffer(String)
 */
export function base64ToArrayBuffer(base64Url: string) {
  if (!base64Url || !isString(base64Url)) {
    return null
  }
  if (base64Url.split(',').length > 1 && base64Url.startsWith('data:')) {
    base64Url = window.btoa(base64Url)
  }
  const padding = '='.repeat((4 - (base64Url.length % 4)) % 4)
  const base64 = (base64Url + padding).replace(/\-/g, '+').replace(/_/g, '/')

  const rawData = window.atob(base64)
  const outputArray = new Uint8Array(rawData.length)

  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i)
  }
  return outputArray
}

/**
 * @function    blob转file
 * @param       { Blob }    需要转换的Blob
 * @param       { String }  文件名称 注：需要正确的文件后缀名 如 1.png
 * @returns     { File  }   file
 * @example     blobToFile(Blob, '1.png')
 */
export function blobToFile(blob: Blob, fileName: string) {
  if (!(blob instanceof Blob)) {
    throw '请传入正确的Blob'
  }
  if (!fileName) {
    throw '请传入文件名'
  } else if (fileName.indexOf('.') === -1) {
    throw '文件名需要后缀名'
  }
  const { type } = blob
  return new File([blob], fileName, { type: type })
}

/**
 * @function    blob转url
 * @param       { Blob }    需要转换的Blob
 * @returns     { Promise } Promise<{base64Url: string, blobUrl:string}>
 * @example     blobToUrl(Blob)
 */
export function blobToUrl(blob: Blob): Promise<{ base64Url: string; blobUrl: string }> {
  if (!(blob instanceof Blob)) {
    throw '请传入正确的Blob'
  }
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader()
    fileReader.readAsDataURL(blob)
    fileReader.onload = (e: any) => {
      resolve({
        base64Url: e.target.result,
        blobUrl: URL.createObjectURL(blob),
      })
    }
    fileReader.onerror = () => {
      reject(new Error('blobToBase64 error'))
    }
  })
}

/**
 * @function  下载资源
 * @param     { Blob | String } blob Blob对象或者下载的url
 * @param     { String }        fileName 下载的文件名称
 * @returns   { undefined }
 * @example     downLoadResource( Blob| String, '1')
 */
export function downLoadResource(blob: Blob | string, fileName: string): void | undefined {
  if (!isString(blob) && !(blob instanceof Blob)) {
    return undefined
  }
  const a = document.createElement('a')
  let objectUrl = isString(blob) ? blob : window.URL.createObjectURL(blob)
  a.download = fileName || '下载'
  a.href = objectUrl
  a.click()
  window.URL.revokeObjectURL(objectUrl)
  a.remove()
}
